// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.

#include <vector>
#include <random>

#if defined(PLATFORM_WINDOWS)
#include <windows.h>
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
#include <unistd.h>
#endif

#include "Platform/TempDirectory.h"

static std::string GenerateRandomName(const std::string& prefix)
{
    static thread_local std::mt19937 gen{ std::random_device{}() };
    // Generate a random 6-digit integer in the range [100000, 999999]
    std::uniform_int_distribution<> dis(100000, 999999);
    return prefix + std::to_string(dis(gen));
}

namespace Platform {

Path TempDirectory::GetSystemTempDir()
{
#if defined(PLATFORM_WINDOWS)
    char buffer[MAX_PATH];
    DWORD len = GetTempPathA(MAX_PATH, buffer);
    if (len == 0 || len > MAX_PATH) {
        throw std::runtime_error("GetTempPathA failed");
    }
    return Path(buffer);
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
    const char *tmp = std::getenv("TMPDIR");
    if (!tmp) {
        return "/tmp";
    }

    Path p(tmp);
    if (p.empty() || !exists(p) || !is_directory(p)) {
        return "/tmp";
    }

    return p;
#endif
}

TempDirectory::TempDirectory()
{
    Path base = GetSystemTempDir();

#if defined(PLATFORM_WINDOWS)
    // Try up to 100 times to create a unique temp directory
    for (int attempt = 0; attempt < 100; ++attempt) {
        Path candidate = base / GenerateRandomName("tmpdir_");
        std::error_code ec;
        if (create_directory(candidate, ec)) {
            path = candidate;
            return;
        }
    }
    throw std::runtime_error("Failed to create unique temp directory");
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
    std::string tmpl = (base / "tmpdir_XXXXXX").string();
    std::vector<char> pathbuf(tmpl.begin(), tmpl.end());
    pathbuf.push_back('\0');

    const auto result = mkdtemp(pathbuf.data());
    if (!result) {
        throw std::runtime_error("mkdtemp failed");
    }

    path = Path(result);
#endif
}

TempDirectory::~TempDirectory()
{
    std::error_code ec;
    remove_all(path, ec);
}

TempDirectory::TempDirectory(TempDirectory&& other) noexcept : path(std::move(other.path))
{
    other.path.clear();
}

TempDirectory& TempDirectory::operator=(TempDirectory&& other) noexcept
{
    if (this != &other) {
        path = std::move(other.path);
        other.path.clear();
    }
    return *this;
}

const Path& TempDirectory::GetPath() const
{
    return path;
}

Path TempDirectory::GenerateUniqueFilePath() const
{
    // Try up to 100 times to find a non-existent file name in the temp directory
    for (int attempt = 0; attempt < 100; ++attempt) {
        Path candidate = path / GenerateRandomName("tmp_");
        if (!exists(candidate)) {
            return candidate;
        }
    }
    throw std::runtime_error("Failed to generate unique file name in temp directory");
}

Path operator/(const TempDirectory& dir, const Path& sub)
{
    return dir.GetPath() / sub;
}

Path operator/(const TempDirectory& dir, const std::string &sub)
{
    return dir.GetPath() / sub;
}

} // Platform